package lnet

import (
	"net"
	"sync"
)

const (
	sessionMapNum = 32
)

type Manager struct {
	sessionMaps [sessionMapNum]sessionMap
	closeOnce   sync.Once
	closeWait   sync.WaitGroup
}

type sessionMap struct {
	sync.RWMutex
	sessions map[uint64]*Session
	closed   bool
}

func NewManager() *Manager {
	m := new(Manager)
	for i := 0; i < len(m.sessionMaps); i++ {
		m.sessionMaps[i].sessions = make(map[uint64]*Session)
	}
	return m
}

func (m *Manager) Close() {
	m.closeOnce.Do(func() {
		for i := 0; i < sessionMapNum; i++ {
			smap := &m.sessionMaps[i]
			smap.Lock()
			smap.closed = true
			for _, sess := range smap.sessions {
				sess.Close()
			}
			smap.Unlock()
		}
		m.closeWait.Wait()
	})
}

func (m *Manager) NewSession(codec Codec, conn net.Conn, sendChanSize int) *Session {
	sess := newSession(m, codec, conn, sendChanSize)
	m.putSession(sess)
	return sess
}

func (m *Manager) GetSession(sessionId uint64) *Session {
	smap := &m.sessionMaps[sessionId%sessionMapNum]
	smap.RLock()
	defer smap.RUnlock()

	return smap.sessions[sessionId]
}

func (m *Manager) putSession(sess *Session) {
	smap := &m.sessionMaps[sess.id%sessionMapNum]
	smap.Lock()
	defer smap.Unlock()

	if smap.closed {
		sess.Close()
		return
	}
	smap.sessions[sess.id] = sess
	m.closeWait.Add(1)
}

func (m *Manager) delSession(sess *Session) {
	smap := &m.sessionMaps[sess.id%sessionMapNum]
	smap.Lock()
	defer smap.Unlock()

	delete(smap.sessions, sess.id)
	m.closeWait.Done()
}
